表单组件:响应式数据双向绑定
概述
本节在已完成 Form 布局的基础上,实现表单响应式数据与组件的双向绑定。核心是通过 Vue 3 的 defineModel 和 reactive 机制,将 schema 定义的数据结构映射为可交互的表单值。
从静态 Schema 到响应式对象
改造思路
将原本写在 template 中的静态 schema 提取为 reactive 响应式对象,使数据变化能自动反映到 UI 上。
// 将 schema 从 template 内联改为响应式定义
import { reactive } from 'vue'
const schema = reactive([
{
label: '用户名',
field: 'username',
type: 'input',
value: '',
},
{
label: '资源类型',
field: 'resources',
type: 'checkbox',
value: [],
options: [
{ label: '文档', value: 'doc' },
{ label: '视频', value: 'video' },
],
},
])
typescript
FormItem 组件的 modelValue 绑定
问题分析
初始实现中 modelValue 默认为空值,导致 Checkbox 组件无法正确绑定——所有选项被默认选中且无法切换。
解决方案
使用 defineModel 实现双向绑定,并在 onBeforeMount 生命周期中设置初始值:
// VFormItem.vue
import { defineModel, onBeforeMount } from 'vue'
interface FormItemProps {
field: string
type: string
value: any
}
const props = defineProps<FormItemProps>()
// 使用 defineModel 创建双向绑定
const modelValue = defineModel<any>('modelValue')
onBeforeMount(() => {
// 从 props.value 读取初始值
if (modelValue.value === undefined || modelValue.value === '') {
modelValue.value = props.value
}
})
typescript
FormLayout 中的数据传递
// VFormLayout.vue
const modelValue = defineModel<any>('modelValue')
// 将 modelValue 传递给 FormItem
<VFormItem
v-for="item in schema"
:key="item.field"
v-model:model-value="modelValue[item.field]"
:field="item.field"
:type="item.type"
:value="item.value"
/>
typescript
双向绑定数据流
schema (reactive)
│
▼
useForm(schema)
│
├── model (响应式表单值)
│ │
│ ▼
│ VFormLayout ── v-model ──> VFormItem
│ │ │
│ │◄──── modelValue ◄───────┘
│ │
│ modelValue[field] = newValue
│
└── formValue (计算属性,用于外部读取)
text
类型定义优化
| 改造项 | 改造前 | 改造后 |
|---|---|---|
| modelValue 类型 | Ref<string> | Ref<any> |
| 初始值来源 | 硬编码空字符串 | 从 props.value 读取 |
| 绑定方式 | v-model 直传 | defineModel 双向绑定 |
| 生命周期处理 | 无 | onBeforeMount 赋值 |
关键要点
defineModel是 Vue 3.4+ 提供的简化双向绑定的宏,替代了传统的props + emit('update:modelValue')模式- 表单值的类型应为
any,以适配不同表单控件(input、checkbox、date-picker 等)的数据格式 onBeforeMount中赋初始值确保组件挂载前数据已就绪- Checkbox 绑定的值应为数组类型,否则会导致全选和无法切换的问题
↑